all repos — caroster @ 8b3a718187d3e9765687e3a91de927242e423fbc

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Linkify from 'linkify-react';
  3import Tooltip from '@mui/material/Tooltip';
  4import IconButton from '@mui/material/IconButton';
  5import Box from '@mui/material/Box';
  6import Link from '@mui/material/Link';
  7import Card from '@mui/material/Card';
  8import Container from '@mui/material/Container';
  9import TextField from '@mui/material/TextField';
 10import Typography from '@mui/material/Typography';
 11import TuneIcon from '@mui/icons-material/Tune';
 12import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 13import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
 14import {useTheme} from '@mui/material/styles';
 15import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 16import {PropsWithChildren, useState} from 'react';
 17import {useTranslation} from 'next-i18next';
 18import pageUtils from '../../../lib/pageUtils';
 19import DetailsLink from '../../../containers/DetailsLink';
 20import ShareEvent from '../../../containers/ShareEvent';
 21import PlaceInput from '../../../containers/PlaceInput';
 22import LangSelector from '../../../components/LangSelector';
 23import usePermissions from '../../../hooks/usePermissions';
 24import useEventStore from '../../../stores/useEventStore';
 25import useToastStore from '../../../stores/useToastStore';
 26import EventLayout, {TabComponent} from '../../../layouts/Event';
 27import {
 28  EventByUuidDocument,
 29  useUpdateEventMutation,
 30} from '../../../generated/graphql';
 31import {langLocales} from '../../../locales/constants';
 32import {getLocaleForLang} from '../../../lib/getLocale';
 33
 34interface Props {
 35  eventUUID: string;
 36  announcement?: string;
 37}
 38
 39const Page = (props: PropsWithChildren<Props>) => {
 40  return <EventLayout {...props} Tab={DetailsTab} />;
 41};
 42
 43const DetailsTab: TabComponent<Props> = ({}) => {
 44  const {t} = useTranslation();
 45  const {
 46    userPermissions: {canEditEventDetails},
 47  } = usePermissions();
 48  const theme = useTheme();
 49  const [updateEvent] = useUpdateEventMutation();
 50  const addToast = useToastStore(s => s.addToast);
 51  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 52  const event = useEventStore(s => s.event);
 53  const [isEditing, setIsEditing] = useState(false);
 54
 55  if (!event) return null;
 56
 57  const hasGeoloc = event.latitude && event.longitude;
 58
 59  const onSave = async e => {
 60    try {
 61      const {uuid, ...data} = event;
 62      delete data.linkedEvent;
 63      delete data.isReturnEvent;
 64      const {
 65        id,
 66        travels,
 67        waitingPassengers,
 68        __typename,
 69        administrators,
 70        passengers,
 71        ...input
 72      } = data;
 73      await updateEvent({
 74        variables: {
 75          uuid,
 76          eventUpdate: {
 77            ...input,
 78          },
 79        },
 80        refetchQueries: ['eventByUUID'],
 81      });
 82      setIsEditing(false);
 83    } catch (error) {
 84      console.error(error);
 85      addToast(t('event.errors.cant_update'));
 86    }
 87  };
 88
 89  const modifyButton = isEditing ? (
 90    <Tooltip
 91      title={t('event.details.save')}
 92      sx={{
 93        position: 'absolute',
 94        top: theme.spacing(2),
 95        right: theme.spacing(2),
 96      }}
 97    >
 98      <IconButton color="primary" onClick={onSave}>
 99        <CheckCircleOutlineIcon />
100      </IconButton>
101    </Tooltip>
102  ) : (
103    <Tooltip
104      title={t('event.details.modify')}
105      sx={{
106        position: 'absolute',
107        top: theme.spacing(2),
108        right: theme.spacing(2),
109      }}
110    >
111      <IconButton color="primary" onClick={() => setIsEditing(true)}>
112        <TuneIcon />
113      </IconButton>
114    </Tooltip>
115  );
116
117  return (
118    <Box
119      sx={{
120        position: 'relative',
121      }}
122    >
123      <Container
124        sx={{
125          p: 4,
126          mt: 6,
127          mb: 11,
128          mx: 0,
129          [theme.breakpoints.down('md')]: {
130            p: 2,
131            mt: 13,
132          },
133        }}
134      >
135        <Card
136          sx={{
137            position: 'relative',
138            maxWidth: '100%',
139            width: '480px',
140            p: 2,
141          }}
142        >
143          <Typography variant="h4" pb={2}>
144            {t('event.details')}
145          </Typography>
146          {canEditEventDetails() && modifyButton}
147          {(isEditing || event.name) && (
148            <Box pt={2} pr={1.5}>
149              <Typography variant="overline">
150                {t('event.fields.name')}
151              </Typography>
152              <Typography>
153                {isEditing ? (
154                  <TextField
155                    size="small"
156                    fullWidth
157                    value={event.name}
158                    onChange={e => setEventUpdate({name: e.target.value})}
159                    name="name"
160                    id="EditEventName"
161                  />
162                ) : (
163                  <Typography id="EventName">{event.name}</Typography>
164                )}
165              </Typography>
166            </Box>
167          )}
168          {(isEditing || event.date) && (
169            <Box pt={2} pr={1.5}>
170              <Typography variant="overline">
171                {t('event.fields.date')}
172              </Typography>
173              {isEditing ? (
174                <Typography>
175                  <DatePicker
176                    slotProps={{
177                      textField: {
178                        size: 'small',
179                        id: `EditEventDate`,
180                        fullWidth: true,
181                        placeholder: t('event.fields.date_placeholder'),
182                      },
183                    }}
184                    format="DD/MM/YYYY"
185                    value={moment(event.date)}
186                    onChange={date =>
187                      setEventUpdate({
188                        date: !date ? null : moment(date).format('YYYY-MM-DD'),
189                      })
190                    }
191                  />
192                </Typography>
193              ) : (
194                <Box position="relative">
195                  <Typography id="EventDate">
196                    {moment(event.date).format('DD/MM/YYYY')}
197                  </Typography>
198                </Box>
199              )}
200            </Box>
201          )}
202          {(isEditing || event.address) && (
203            <Box pt={2} pr={1.5}>
204              <Typography variant="overline">
205                {t('event.fields.address')}
206              </Typography>
207              {isEditing ? (
208                <PlaceInput
209                  place={event.address}
210                  latitude={event.latitude}
211                  longitude={event.longitude}
212                  onSelect={({place, latitude, longitude}) =>
213                    setEventUpdate({
214                      address: place,
215                      latitude,
216                      longitude,
217                    })
218                  }
219                />
220              ) : (
221                <Box position="relative">
222                  <Typography
223                    id="EventAddress"
224                    title={t`placeInput.noCoordinates`}
225                    sx={{
226                      pr: 3,
227                      display: 'inline-flex',
228                      alignItems: 'center',
229                      columnGap: 1,
230                    }}
231                  >
232                    <Link
233                      target="_blank"
234                      rel="noreferrer"
235                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
236                        event.address
237                      )}`}
238                      onClick={e => e.preventDefault}
239                    >
240                      {event.address}
241                    </Link>
242                    {!hasGeoloc && (
243                      <InfoOutlinedIcon fontSize="small" color="warning" />
244                    )}
245                  </Typography>
246                </Box>
247              )}
248            </Box>
249          )}
250          {(isEditing || event.description) && (
251            <Box pt={2} pr={1.5}>
252              <Typography variant="overline">
253                {t('event.fields.description')}
254              </Typography>
255              {isEditing ? (
256                <Typography>
257                  <TextField
258                    fullWidth
259                    multiline
260                    maxRows={4}
261                    inputProps={{maxLength: 250}}
262                    value={event.description || ''}
263                    onChange={e =>
264                      setEventUpdate({description: e.target.value})
265                    }
266                    id={`EditEventDescription`}
267                    name="description"
268                  />
269                </Typography>
270              ) : (
271                <Typography
272                  id="EventDescription"
273                  sx={{pr: 3, whiteSpace: 'pre-line'}}
274                >
275                  <Linkify options={{render: DetailsLink}}>
276                    {event.description}
277                  </Linkify>
278                </Typography>
279              )}
280            </Box>
281          )}
282          {(isEditing || event.lang) && (
283            <Box pt={2} pr={1.5}>
284              <Typography variant="overline">
285                {t('event.fields.lang')}
286              </Typography>
287              {isEditing ? (
288                <LangSelector
289                  value={event.lang}
290                  onChange={lang => setEventUpdate({lang})}
291                />
292              ) : (
293                <Typography id="EventLang" sx={{pr: 3}}>
294                  {langLocales[event.lang]}
295                </Typography>
296              )}
297            </Box>
298          )}
299          {!isEditing && !!event.email && (
300            <Box pt={2} pr={1.5}>
301              <Typography variant="overline">
302                {t('options.plus.creator')}
303              </Typography>
304              <Typography id="EventLang" sx={{pr: 3}}>
305                {event.email}
306              </Typography>
307            </Box>
308          )}
309          {!isEditing && (
310            <ShareEvent
311              title={`Caroster ${event.name}`}
312              sx={{width: '100%', mt: 2}}
313            />
314          )}
315        </Card>
316      </Container>
317    </Box>
318  );
319};
320
321export const getServerSideProps = pageUtils.getServerSideProps(
322  async (context, apolloClient) => {
323    const {uuid} = context.query;
324    const {host = ''} = context.req.headers;
325    let event = null;
326
327    // Fetch event
328    try {
329      const {data} = await apolloClient.query({
330        query: EventByUuidDocument,
331        variables: {uuid},
332      });
333      event = data?.eventByUUID?.data;
334    } catch (error) {
335      return {
336        notFound: true,
337      };
338    }
339
340    const description = await getLocaleForLang(
341      event?.attributes?.lang,
342      'meta.description'
343    );
344
345    return {
346      props: {
347        eventUUID: uuid,
348        metas: {
349          title: event?.attributes?.name || '',
350          description,
351          url: `https://${host}${context.resolvedUrl}`,
352        },
353      },
354    };
355  }
356);
357export default Page;